﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;

namespace USB_HID
{

    public delegate void RecordNumberEventHanler(UInt32 nNumber);
    public delegate void RecordTimeEventHanler(UInt32 nNumber, byte[] bTime);
    public delegate void RecordDataEventHanler(string strData);

    class Communication
    {
        const byte COMM_SCMD_INQUIRE = 0X81;
        const byte COMM_SCMD_ACK = 0X82;

        const byte COMM_NCMD_SYNC_TIME = 0X01;
        const byte COMM_NCMD_RECORD_TIME = 0X02;
        const byte COMM_NCMD_RECORD_DATA = 0X03;
        const byte COMM_NCMD_RECORD_NUMBER = 0X04;


        Hid usbHID = new Hid();
        //StreamWriter writeText = null;
        //string recordFilePath = null;

        public event RecordNumberEventHanler COMM_ReceiveRecordNumber;
        public event RecordTimeEventHanler COMM_ReceiveRecordTime;
        public event RecordDataEventHanler COMM_ReceiveRecordData;

        //public UInt32 recordNumber;
        //public byte[] recordTime;

        public Communication()
        {
            //Hid usbHID = ;

            usbHID.DataReceived += usbHID_DataReceived;
            usbHID.DeviceRemoved += usbHID_DeviceRemoved;

        }


        public bool usbSearchDeveice()
        {
            return usbHID.CheckDevicePresent();
        }

        public event EventHandler COMM_DeviceRemoved;
        void usbHID_DeviceRemoved(object sender, EventArgs e)
        {
            Console.WriteLine("Deveice Removed");

            if (COMM_DeviceRemoved != null)
            {
                COMM_DeviceRemoved(this, e);
            }
        }

        void writeRecord(byte[] data)
        {
            
            UInt32 totalNumber = (UInt32)(data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24));
            UInt32 latestNo = (UInt32)(data[4] | (data[5] << 8) | (data[6] << 16) | (data[7] << 24));

            if (latestNo == 0)  //第一包数据
            {
                //writeText = new StreamWriter(recordFilePath, false, Encoding.GetEncoding("GB2312"));
                //Console.WriteLine(recordFilePath);
            }

            //if (writeText != null)
            {
                string record = "No" + latestNo.ToString("0000") +"* ";
                float value;
                int temp;

                temp = (data[8] | data[9] << 8);
                record += temp.ToString("00") +".";

                temp = data[10];
                record += temp.ToString("00") + ".";

                temp = data[11];
                record += temp.ToString("00") + ".";

                temp = data[12];
                record += temp.ToString("00") + ":";

                temp = data[13];
                record += temp.ToString("00") + ":";

                temp = data[14];
                record += temp.ToString("00") + "  ";

                record += "温度：";
                temp = data[16] | (data[17] << 8) | (data[18] << 16) | (data[19] << 24);
                value = temp / 10.0f;
                record += value.ToString("0.0");
                record += "℃  ";

                record += "湿度：";
                temp = data[20] | (data[21] << 8) | (data[22] << 16) | (data[23] << 24);
                value = temp / 10.0f;
                record += value.ToString("0.0");
                record += "%";

                if (COMM_ReceiveRecordData != null)
                {
                    COMM_ReceiveRecordData(record);
                }

                //writeText.WriteLine(record);

                if (latestNo >= totalNumber - 1)
                {
                    //writeText.Close();
                    //writeText = null;
                }
                Console.WriteLine("record No {0:G}", latestNo);
            }
            

        }

        

        void sendAck(byte status)
        {
            byte[] bPackBuff = new byte[64];
            byte[] bSendBuff = new byte[128];
            
            UInt16 nLength = 0;
            COMM_DataProcess DataEsc = new COMM_DataProcess();

            bPackBuff[0] = status;

            nLength = 1;

            bSendBuff = DataEsc.DataPackage(COMM_SCMD_ACK, bPackBuff, nLength);

            report myRP = new report(0, bSendBuff);

            usbHID.Write(myRP);
        }

        void cmdProcess(byte cmd, byte[] data)
        {
            switch (cmd)
            {
                case COMM_NCMD_RECORD_TIME:
                    {
                        sendAck(1);
                        if (COMM_ReceiveRecordTime != null)
                        {
                            UInt32 number = (UInt32)(data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24));
                            byte[] temp = new byte[number * 2];
                            int i;
                            for(i = 0; i < number * 2; i++)
                            {
                                temp[i] = data[4 + i];
                            }
                            COMM_ReceiveRecordTime(number, temp);
                        }
                    }
                    break;


                case COMM_NCMD_RECORD_DATA:
                    {
                        sendAck(1);
                        writeRecord(data);
                    }
                    break;

                case COMM_NCMD_RECORD_NUMBER:
                    {
                        sendAck(1);

                        if (COMM_ReceiveRecordNumber != null)
                        {
                            UInt32 number = (UInt32)(data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24));
                            COMM_ReceiveRecordNumber(number);
                        }
                    }
                    break;
            }
        }

        void usbHID_DataReceived(object sender, report e)
        {
            int i;
            int nLength = e.reportBuff.GetLength(0);

            COMM_DataProcess DataEsc = new COMM_DataProcess();

            for (i = 0; i < nLength; i++)
            {
                DataEsc.PacketParsing(e.reportBuff[i]);
            }

            if (DataEsc.packParsingStatus)
            {
                cmdProcess(DataEsc.rxCMD, DataEsc.rxData);
            }
        }


        public void SyncTime()
        {
            byte[] bPackBuff = new byte[64];
            byte[] bSendBuff = new byte[128];
            
            UInt16 nLength = 0;
            COMM_DataProcess DataEsc = new COMM_DataProcess();

            DateTime timeNow = DateTime.Now;
            bPackBuff[0] = (byte)(timeNow.Year & 0xff);
            bPackBuff[1] = (byte)((timeNow.Year >> 8) & 0xff);
            bPackBuff[2] = (byte)(timeNow.Month);
            bPackBuff[3] = (byte)(timeNow.Day);
            bPackBuff[4] = (byte)(timeNow.Hour);
            bPackBuff[5] = (byte)(timeNow.Minute);
            bPackBuff[6] = (byte)(timeNow.Second);

            nLength = 7;

            bSendBuff = DataEsc.DataPackage(0x01, bPackBuff, nLength);

            report myRP = new report(0, bSendBuff);

            usbHID.Write(myRP);

            //usbHID.WriteUSBHID(bSendBuff);
        }


        public void GetRecord()
        {
            byte[] bPackBuff = new byte[64];
            byte[] bSendBuff = new byte[128];

            //recordFilePath = path;

            UInt16 nLength = 0;
            COMM_DataProcess DataEsc = new COMM_DataProcess();

            //DateTime timeNow = DateTime.Now;
            bPackBuff[0] = 0x03;

            nLength = 1;

            bSendBuff = DataEsc.DataPackage(0x81, bPackBuff, nLength);

            report myRP = new report(0, bSendBuff);

            usbHID.Write(myRP);

            //usbHID.WriteUSBHID(bSendBuff);
        }


        public void GetRecordNumber()
        {
            byte[] bPackBuff = new byte[64];
            byte[] bSendBuff = new byte[128];


            UInt16 nLength = 0;
            COMM_DataProcess DataEsc = new COMM_DataProcess();

            bPackBuff[0] = 0x04;

            nLength = 1;

            bSendBuff = DataEsc.DataPackage(0x81, bPackBuff, nLength);

            report myRP = new report(0, bSendBuff);

            usbHID.Write(myRP);
        }

        public void GetRecordTime()
        {
            byte[] bPackBuff = new byte[64];
            byte[] bSendBuff = new byte[128];


            UInt16 nLength = 0;
            COMM_DataProcess DataEsc = new COMM_DataProcess();

            bPackBuff[0] = 0x02;

            nLength = 1;
             
            bSendBuff = DataEsc.DataPackage(0x81, bPackBuff, nLength);

            report myRP = new report(0, bSendBuff);

            usbHID.Write(myRP);
        }
        public void ClearRecord()
        {
            byte[] bPackBuff = new byte[64];
            byte[] bSendBuff = new byte[128];


            UInt16 nLength = 0;
            COMM_DataProcess DataEsc = new COMM_DataProcess();

            bPackBuff[0] = 0x05;

            nLength = 1;

            bSendBuff = DataEsc.DataPackage(0x05, bPackBuff, 0);

            report myRP = new report(0, bSendBuff);

            usbHID.Write(myRP);
        }

        public void SyncRecordTime(UInt32 length, byte[] data)
        {
            byte[] bPackBuff = new byte[64];
            byte[] bSendBuff = new byte[128];
            UInt16 nLength = 0;
            COMM_DataProcess DataEsc = new COMM_DataProcess();

            //bPackBuff = System.BitConverter.GetBytes(length);
            bPackBuff[0] = (byte)(length & 0xff);
            bPackBuff[1] = (byte)((length >> 8) & 0xff);
            bPackBuff[2] = (byte)((length >> 16) & 0xff);
            bPackBuff[3] = 0;//(byte)((length  >> 32) & 0xff);

            data.CopyTo(bPackBuff, 4);

            nLength = (UInt16)(length * 2 + 4);

            bSendBuff = DataEsc.DataPackage(0x02, bPackBuff, nLength);

            report myRP = new report(0, bSendBuff);

            usbHID.Write(myRP);
        }
    }


    class COMM_DataProcess
    {
        const byte ESC = 0xDD;       //
        const byte STX = 0xAA;       //
        const byte ETX = 0x30;       //

        UInt32 DisposeESC(UInt32 nLength, ref byte[] pOut, byte[] pIn)
        {
            UInt32 i = 0;
            UInt16 temp;
            UInt32 out_length = 0;
            for (i = 0; i < nLength; i++)
            {
                if (pIn[i] == ESC || pIn[i] == STX || pIn[i] == ETX)  //need to  escape data processing
                {
                    temp = (UInt16)(pIn[i] | 0x100);
                    pOut[out_length] = ESC;
                    out_length++;

                    pOut[out_length] = (byte)((temp - ESC) & 0XFF);
                }
                else
                {
                    pOut[out_length] = pIn[i];
                }
                out_length++;
            }
            
            return out_length;
        }

        struct DataESC
        {
            public UInt32 nLength;
            public byte[] bBuff;
        };

        public byte[] DataPackage(byte CMD, byte[] Pack_Buf, UInt16 Length)
        {
            DataESC escCMD;
            DataESC escPack;
            DataESC escVerify;

            UInt32 i = 0;
            UInt32 length = 0;
            byte Verify = 0;

            byte[] bResultBuff = new byte[128];
            byte[] temp_buff = new byte[64];

            escCMD.bBuff = new byte[64];
            escPack.bBuff = new byte[64];
            escVerify.bBuff = new byte[64];
            
            if (Length > 64)
            {
                return bResultBuff;
            }

            Verify = 0;
            
            for (i = 0; i < Length; i++)               //calculate check sum
            {
                Verify += Pack_Buf[i];
            }
            Verify = (byte)(~Verify); 
            
            
            bResultBuff[0] = STX;   //数据包头
        
            temp_buff[0] = CMD;                        
            temp_buff[1] = (byte)(Length & 0xff);
            temp_buff[2] = (byte)((Length >> 8) & 0xff);
            
            length = 1;
            escCMD.nLength = DisposeESC(3, ref escCMD.bBuff, temp_buff);        //CMD、 长度进行转意

            escPack.nLength = DisposeESC(Length, ref escPack.bBuff, Pack_Buf); //包内容转义
            
            temp_buff[0] = Verify;
            
            escVerify.nLength = DisposeESC(1, ref escVerify.bBuff, temp_buff);  //校验码转义

            escCMD.bBuff.CopyTo(bResultBuff, length);
            length += escCMD.nLength;

            escPack.bBuff.CopyTo(bResultBuff, length);
            length += escPack.nLength;

            escVerify.bBuff.CopyTo(bResultBuff, length);
            length += escVerify.nLength;

            bResultBuff[length++] = ETX;

            return bResultBuff;
        }


        public bool packParsingStatus;
        public byte  rxCMD;
        public int rxLength;
        public byte[] rxData = new byte[128];

        int checkSum;
        int dataCnt;
        int packParsingStep;
        byte data = 0;
        byte last_data;
        public void PacketParsing(byte data_in)
        {
            if (data_in == STX && packParsingStep != 0)     // find STX
            {
                //ReceiveError(data_pack);
                packParsingStep = 0;
                data = data_in;
            } 
            else if(data_in == ESC)
            {
                last_data = data_in;
                return;
            }
            else if(last_data == ESC)
            {
                data = (byte)((data_in + ESC) & 0xFF);
                last_data = 0;
            }
            else
            {
                data = data_in;
            }
            
        
            switch(packParsingStep)
            {
                case 0:
                    if (data == STX)                    //header check
                    {
                        packParsingStep++;
                        packParsingStatus = false;
                    }
                    break;
                case 1:
                    rxCMD = data;              //get cmd
                    packParsingStep++;
                    break;
                    
                case 2:
                    rxLength = data;           //get length
                    packParsingStep++;
                    break;
                
                case 3:
                    rxLength |= (data << 8);
                    dataCnt = 0;
                    checkSum = 0;
                    if (rxLength == 0)
                    {
                        packParsingStep = 0x21;
                    }
                    else 
                    {
                        packParsingStep = 0x20;
                    }
                    break;
                
        
                    
                case 0x20:
                    
                    rxData[dataCnt] = data;
                    checkSum += data;
                    dataCnt++;
                    if (dataCnt >= rxLength)       //GET DATA
                    {
                        packParsingStep = 0x21;
                    }
                    break;
                    
                case 0x21:
                    
                    //DBG_PRINTF("check sum = %x\r\n", (uint8_t)(~(data_pack->check_sum & 0xFF)));
                    
                    if ((byte)(~(checkSum & 0xFF)) == data)           //check sum hight bit
                    {
                        packParsingStep = 0xff;
                    }
                    else
                    {
                        //ReceiveError(data_pack);
                        packParsingStep = 0;
                        packParsingStatus = false;
                    }
                    break;
                    
            
                
                case 0xff:
                    if (data == ETX)                                // tail check                           
                    {
                        packParsingStep = 0;
                        packParsingStatus = true;
                        
                    }
                    else
                    {
                        packParsingStep = 0;
                        packParsingStatus = false;
                    }
                    break;
            }
            if(data != ESC)
            {
                last_data = data;
            }
        }

    }
}
